<template>
  <div class="tree-qvil">
    <div v-if="showHeader" class="tree-header">
      <span>{{ title }}</span>
      <el-icon class="refresh-icon" @click="handleRefresh"><refresh /></el-icon>
    </div>
    <el-input
      v-if="showSearch"
      v-model="searchKeyword"
      class="search-input"
      clearable
      :placeholder="searchPlaceholder"
      :prefix-icon="Search"
      @input="handleSearchInput"
    />
    <div v-if="loading" class="loading-container">
      <el-skeleton animated :rows="5" />
    </div>
    <template v-else>
      <el-scrollbar :class="scrollbarClass" :height="scrollHeight">
        <el-tree
          ref="treeRef"
          v-loading="treeLoading"
          :data="treeData"
          :default-expanded-keys="expandedKeys"
          highlight-current
          :lazy="isLazy"
          :load="isLazy ? loadNode : undefined"
          node-key="id"
          :props="treeProps"
          @node-click="handleNodeClick"
        >
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <el-icon v-if="data.isLeaf"><document /></el-icon>
              <el-icon v-else-if="node.expanded"><folder-opened /></el-icon>
              <el-icon v-else><folder /></el-icon>
              <span :title="node.label">{{ node.label }}</span>
              <slot :data="data" name="node-right" :node="node"></slot>
            </div>
          </template>
        </el-tree>
        <div class="tree-bottom-safe-area"></div>
      </el-scrollbar>
    </template>
  </div>
</template>

<script setup lang="ts">
import { Document, Folder, FolderOpened, Refresh, Search } from '@element-plus/icons-vue'
import type { ElTree } from 'element-plus'
import { computed, onMounted, ref, watch } from 'vue'
import type { TreeNode, TreeQvilEvents, TreeQvilProps } from './types'
import request from '/@/utils/request'

const props = withDefaults(defineProps<TreeQvilProps>(), {
  modelValue: '',
  title: '树形选择',
  showHeader: true,
  showSearch: true,
  searchPlaceholder: '请输入关键词搜索',
  scrollHeight: 'calc(100vh - 240px)',
  requestApi: '',
  searchApi: '',
  isLazy: true,
  checkStrictly: false,
  customRequest: undefined,
  customSearch: undefined,
  customLoadNode: undefined,
  nodeKey: 'id',
  labelKey: 'name',
  childrenKey: 'children',
  params: () => ({}),
  dataFilter: (data) => data,
})

const emit = defineEmits<TreeQvilEvents>()

// 状态变量
const loading = ref(false)
const treeLoading = ref(false)
const treeData = ref<TreeNode[]>([])
const expandedKeys = ref<string[]>([])
const searchTimeout = ref<number | null>(null)
const searchKeyword = ref('')
const currentNodeId = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()

// 计算tree props
const treeProps = computed(() => ({
  children: props.childrenKey,
  label: props.labelKey,
  isLeaf: 'isLeaf',
}))

// 计算scrollbar class
const scrollbarClass = computed(() => `tree-scrollbar${props.scrollbarClass ? ` ${props.scrollbarClass}` : ''}`)

// 监听modelValue变化
watch(
  () => props.modelValue,
  (val) => {
    if (val) {
      currentNodeId.value = val.toString()
      // 等待树加载完成后，尝试高亮选中节点
      if (treeRef.value) {
        treeRef.value.setCurrentKey(val)
      }
    } else {
      currentNodeId.value = ''
      if (treeRef.value) {
        treeRef.value.setCurrentKey(undefined)
      }
    }
  }
)

// 加载初始数据
const loadInitialData = async () => {
  if (!props.requestApi && !props.customRequest) return

  loading.value = true
  try {
    let result
    if (props.customRequest) {
      result = await props.customRequest(props.params)
    } else {
      // 使用request替代fetch
      const response = await request({
        url: props.requestApi,
        method: 'get',
        params: props.params ? props.params : { page: 1, limit: 100, id: null },
      })

      // 从response中提取数据
      result = response.data || response
    }

    // 处理数据
    const processedData = props.dataFilter(result)

    // 更新树数据
    treeData.value = Array.isArray(processedData) ? processedData : []

    // 如果没有数据，显示提示
    if (treeData.value.length === 0) {
      treeData.value = [
        {
          id: 'empty',
          [props.labelKey]: '暂无部门数据',
          isLeaf: true,
        },
      ]
    }

    // 如果有默认选中值，设置当前节点
    if (props.modelValue && treeRef.value) {
      treeRef.value.setCurrentKey(props.modelValue)
    }
  } catch (error) {
    console.error('加载数据失败:', error)
    treeData.value = [
      {
        id: 'error',
        [props.labelKey]: '加载失败，请刷新重试',
        isLeaf: true,
      },
    ]
  } finally {
    loading.value = false
  }
}

// 懒加载节点数据
const loadNode = async (node: any, resolve: (data: any[]) => void) => {
  // 根节点直接返回已加载的数据
  if (node.level === 0) {
    resolve(treeData.value)
    return
  }

  // 调用自定义加载函数或使用默认逻辑
  if (props.customLoadNode) {
    const result = await props.customLoadNode(node)
    resolve(result)
    return
  }

  // 默认加载逻辑
  const { data } = node
  treeLoading.value = true

  try {
    // 使用request替代fetch，并使用正确的params传参
    const response = await request({
      url: props.requestApi,
      method: 'get',
      params: {
        ...props.params,
        parentId: data.id,
        page: 1,
        limit: 100,
      },
    })

    // 从response中提取数据
    const result = response.data || response
    const children = props.dataFilter(result)

    // 如果没有子节点，标记为叶子节点
    if (!children || children.length === 0) {
      data.isLeaf = true
      resolve([])
    } else {
      resolve(children)
    }
  } catch (error) {
    console.error('加载子节点失败:', error)
    resolve([])
  } finally {
    treeLoading.value = false
  }
}

// 搜索数据
const searchData = async (keyword: string) => {
  if (!keyword) {
    // 如果搜索词为空，重新加载初始数据
    await loadInitialData()
    return
  }

  if (!props.searchApi && !props.customSearch) {
    console.warn('未配置搜索API或自定义搜索方法')
    return
  }

  loading.value = true
  try {
    let result
    if (props.customSearch) {
      result = await props.customSearch(keyword, props.params)
    } else {
      // 使用request替代fetch，并使用正确的params传参
      const response = await request({
        url: props.searchApi,
        method: 'get',
        params: {
          ...props.params,
          name: keyword,
          page: 1,
          limit: 50,
        },
      })

      // 从response中提取数据
      result = response.data || response
    }

    // 处理数据
    const searchResults = props.dataFilter(result)

    // 更新树数据
    if (searchResults && searchResults.length > 0) {
      treeData.value = searchResults
    } else {
      // 如果没有搜索结果，显示提示
      treeData.value = [
        {
          id: 'empty-search',
          [props.labelKey]: `未找到"${keyword}"相关结果`,
          isLeaf: true,
        },
      ]
    }
  } catch (error) {
    console.error('搜索失败:', error)
    treeData.value = [
      {
        id: 'error-search',
        [props.labelKey]: '搜索失败，请重试',
        isLeaf: true,
      },
    ]
  } finally {
    loading.value = false
  }
}

// 处理搜索输入
const handleSearchInput = (value: string) => {
  // 防抖处理
  if (searchTimeout.value) {
    clearTimeout(searchTimeout.value)
  }
  searchTimeout.value = setTimeout(() => {
    searchData(value)
  }, 500) as unknown as number
}

// 节点点击事件
const handleNodeClick = (data: TreeNode) => {
  if (data.id === 'error' || data.id === 'empty') {
    return
  }

  currentNodeId.value = data.id.toString()
  emit('node-click', data)
  emit('update:modelValue', data.id)
}

// 刷新树
const handleRefresh = () => {
  searchKeyword.value = ''
  currentNodeId.value = ''
  expandedKeys.value = []
  loadInitialData()
  emit('refresh')
}

// 对外暴露的方法
const setCurrentNode = (nodeId: string | number) => {
  if (treeRef.value) {
    treeRef.value.setCurrentKey(nodeId)
    currentNodeId.value = nodeId.toString()
  }
}

const getCheckedNodes = () => {
  if (treeRef.value) {
    return treeRef.value.getCheckedNodes()
  }
  return []
}

// 初始化
onMounted(() => {
  loadInitialData()
})

// 对外暴露方法
defineExpose({
  treeRef,
  treeData,
  loadInitialData,
  handleRefresh,
  setCurrentNode,
  getCheckedNodes,
})
</script>

<style scoped>
.tree-qvil {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: #fff;
  border-right: 1px solid #ebeef5;
}

.tree-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  font-weight: 500;
  border-bottom: 1px solid #ebeef5;
}

.refresh-icon {
  color: #909399;
  cursor: pointer;
}

.refresh-icon:hover {
  color: #409eff;
}

.search-input {
  width: 100%;
  padding: 12px;
}

.loading-container {
  padding: 16px;
}

.tree-scrollbar {
  overflow: auto;
}

.tree-bottom-safe-area {
  width: 100%;
  height: 20px; /* 底部安全区域高度 */
}

.custom-tree-node {
  display: flex;
  gap: 4px;
  align-items: center;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.custom-tree-node .el-icon {
  flex-shrink: 0;
  font-size: 16px;
  color: #909399;
}

.custom-tree-node span {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

:deep(.el-tree-node__content) {
  height: 32px;
}

:deep(.el-tree-node.is-current > .el-tree-node__content) {
  background-color: #ecf5ff;
}

:deep(.el-tree-node__expand-icon.expanded) {
  transform: rotate(90deg);
}

:deep(.el-scrollbar__wrap) {
  padding-bottom: 5em !important;
}
</style>
